package com.zf.developaccumulate.controller;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/**
 * 代码调用多文件上传接口
 */
@Slf4j
@RestController
@RequestMapping("/file")
public class FileUploadController {

    /**
     * 上传表单，包含多个文件
     */
    @PostMapping(value = "/uploadMultiFile")
    public String upload(FileFormEntity formEntity) throws IOException {
        log.info("username: {}, age: {}", formEntity.getUsername(), formEntity.getAge());
        List<MultipartFile> attachFileList = formEntity.getAttachFileList();
        if (CollectionUtils.isNotEmpty(attachFileList)) {
            for (MultipartFile file : attachFileList) {
                // 文件名
                String originalFilename = file.getOriginalFilename();
                log.info("originalFilename: {}", originalFilename);
                // 获取文件输入流
                InputStream inputStream = file.getInputStream();
                // 读取输入流内容
                byte[] fileBytes = FileCopyUtils.copyToByteArray(inputStream);
                if (fileBytes.length == 0) {
                    continue;
                }
                // 将读取的内容写入一个新的文件中
                FileOutputStream outputStream = new FileOutputStream("D:\\out-" + originalFilename);
                FileCopyUtils.copy(fileBytes, outputStream);
            }
        }
        return "success";
    }


    /**
     * 代码调用上传表单接口
     */
    @PostMapping("/callUploadMultiFile")
    public String callUploadMultiFile() throws IOException {
        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置 Content-Type 为 "multipart/form-data"
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // 使用 FileSystemResource 读取本地文件，创建Resource对象，
        // 这种方式只能从本地已经存在的文件中读取内容，不能获取网络输入流中的内容，太局限，不推荐
        FileSystemResource fileSystemResource1 = new FileSystemResource("D:\\file1.txt");
        FileSystemResource fileSystemResource2 = new FileSystemResource("D:\\file2.txt");

        // 注意：针对 Content-Type 是 "multipart/form-data", "application/x-www-form-urlencoded" （即 form表单） 的所有请求：
        //      RestTemplate 要求必须使用 MultiValueMap 作为请求体，否则报错。原因见 org.springframework.http.converter.FormHttpMessageConverter.canWrite 方法
        //      Content-Type 是其他类型时，可以使用 "实体类 或 Map" 作为请求体。

        // 创建请求体，并添加内容
        MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("username", "李四");
        requestBody.add("age", 13);
        requestBody.add("attachFileList", fileSystemResource1);
        requestBody.add("attachFileList", fileSystemResource2);

        // 创建HttpEntity，组合请求体和请求头
        HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);

        // 创建 RestTemplate
        RestTemplate restTemplate = new RestTemplate();
        // 发送请求
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8109/file/uploadMultiFile", requestEntity, String.class);
        return response.getBody();
    }


    /**
     * 代码调用上传表单接口
     */
    @PostMapping("/callUploadMultiFile2")
    public String callUploadMultiFile2() throws IOException {
        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置 Content-Type 为 "multipart/form-data"
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // 二进制源，可以从文件、网络流中读取数据，更灵活，推荐使用这种方式
        byte[] file1Bytes = Files.readAllBytes(Path.of("D:\\file1.txt"));
        byte[] file2Bytes = Files.readAllBytes(Path.of("D:\\file2.txt"));

        // 创建一个 ByteArrayResource
        ByteArrayResource file1Resource = createByteArrayResource(file1Bytes, "file111.txt");
        ByteArrayResource file2Resource = createByteArrayResource(file2Bytes, "file222.txt");

        // 注意：针对 Content-Type 是 "multipart/form-data", "application/x-www-form-urlencoded"（即 form表单） 的所有请求：
        //      RestTemplate 要求必须使用 MultiValueMap 作为请求体，否则报错。原因见 org.springframework.http.converter.FormHttpMessageConverter.canWrite 方法
        //      Content-Type 是其他类型时，可以使用 "实体类 或 Map" 作为请求体。

        // 创建请求体，并添加内容
        MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("username", "王五");
        requestBody.add("age", 23);
        requestBody.add("attachFileList", file1Resource);
        requestBody.add("attachFileList", file2Resource);

        // 创建HttpEntity，组合请求体和请求头
        HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);

        // 创建 RestTemplate
        RestTemplate restTemplate = new RestTemplate();
        // 发送请求
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8109/file/uploadMultiFile", requestEntity, String.class);
        return response.getBody();
    }

    /**
     * 创建一个 ByteArrayResource 资源
     */
    public ByteArrayResource createByteArrayResource(byte[] content, String filename) throws IOException {
        // 创建一个 ByteArrayResource，必须要重写获取文件名和长度方法
        return new ByteArrayResource(content) {

            /**
             * 如果不重写这个方法，并且文件有一定大小，那么服务端会出现异常
             */
            @Override
            public String getFilename() {
                return filename;
            }

            /**
             * 重写这个方法，RestTemplate 发送请求时会通过这个方法得到文件的大小。
             * 也可以不重写，父类的 contentLength() 的内容如下：
             * <pre>
             * 	  public long contentLength() {
             * 		return this.byteArray.length;
             *    }
             *
             * ByteArrayResource(content) 构造会把 this.byteArray = content,
             * 所以不用重写也可以获取到正确的文件大小
             * </pre>
             */
            @Override
            public long contentLength() {
                return content.length;
            }
        };
    }


    /**
     * 参数
     */
    @Getter
    @Setter
    public static class FileFormEntity {
        private String username;
        private Integer age;
        private List<MultipartFile> attachFileList;
    }


}
